package mes.kanban.service.impl;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.annotation.Resource;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.fasterxml.jackson.core.JsonProcessingException;

import mes.kanban.config.JobConfig;
import mes.kanban.config.KVRedisConfig;
import mes.kanban.entity.MesConfigList;
import mes.kanban.entity.MesMaterial;
import mes.kanban.entity.MesPollingConfig;
import mes.kanban.respository.ConfigListRespository;
import mes.kanban.respository.MaterialRespository;
import mes.kanban.respository.PollingConfigRespository;
import mes.kanban.respository.UserJobRespository;
import mes.kanban.service.PollingConfigServiceI;
import mes.kanban.service.RedisServiceI;
import mes.kanban.util.DateUtil;
import mes.kanban.util.IsNotNullValit;
import mes.kanban.util.JsonUtil;
import mes.kanban.util.PageBean;
import mes.kanban.util.SerializeUtil;
import mes.kanban.vo.MesConfigListVO;
import mes.kanban.vo.MesPollingConfigVO;

/**
 * 报表轮询配置
 * @author zongrui.liu
 */
@Service
public class PollingConfigServiceImpl implements PollingConfigServiceI {
	
	
	private static final Logger log = LoggerFactory.getLogger(PollingConfigServiceImpl.class);

	
	@Autowired
	private PollingConfigRespository  pollingConfigRespository;

	@Autowired
	private ConfigListRespository configListRespository;
	
	@Resource
	private RedisServiceI redisService;
	
	@Resource
	private UserJobRespository userJobRespository;
	
	@Resource
	private MaterialRespository materialRespository;
	

	
	@Override
	public List<MesPollingConfigVO> getConfigList() {
		log.info("reportPollingConfigRespository findAll 查询数据");
		List<MesPollingConfigVO> configVos = new ArrayList<MesPollingConfigVO>();
		List<MesPollingConfig> configs = pollingConfigRespository.findAll();
		MesPollingConfigVO vo = null;
		if(!configs.isEmpty()) {
			for(MesPollingConfig config : configs) {
				vo = new MesPollingConfigVO();
				BeanUtils.copyProperties(config,vo);
				configVos.add(vo);
			}
		}
		
		return configVos;
	}

	@Transactional
	@Override
	public void save(MesPollingConfigVO pollingConfigVO) {
		MesPollingConfig reportPollingConfig = new MesPollingConfig();
		BeanUtils.copyProperties(pollingConfigVO,reportPollingConfig);
		pollingConfigRespository.save(reportPollingConfig);
		log.info("redis 保存");
		
		//保存url关联的id
		redisService.setConfig(pollingConfigVO.getKanbanUrl(), reportPollingConfig.getId());
		BeanUtils.copyProperties(reportPollingConfig,pollingConfigVO);
		
	}

	@Override
	public MesPollingConfigVO findConfigById(String id) {
		
		MesPollingConfigVO vo = null;
		
		String value = redisService.hashGetValue(KVRedisConfig.MES_POLLING_CONFIG.getHaskey(), id);
		
		if (IsNotNullValit.StringIsNotNull(value)) {
			try {
				
				log.info("redis findConfigById 返回");
				
				return (MesPollingConfigVO) JsonUtil.changeJsonToPOJO(value, MesPollingConfigVO.class);
			} catch (IOException e) {
				e.printStackTrace();
				log.error("类型转换异常:"+e.getMessage());
				//从数据库查询数据，然后
				vo = new MesPollingConfigVO();
				MesPollingConfig config = pollingConfigRespository.findOne(id);
				BeanUtils.copyProperties(config,vo);
				log.info("mysql 返回");
				try {
					log.info("redis 保存");
					redisService.hashSetValue(KVRedisConfig.MES_POLLING_CONFIG.getHaskey(), 
							id,
							JsonUtil.changeObjectToJson(vo));
				} catch (JsonProcessingException ee) {
					ee.printStackTrace();
					log.error("redis存储异常，查看："+vo.getId());
				}
			}
		}else {
			vo = new MesPollingConfigVO();
			MesPollingConfig config = pollingConfigRespository.findOne(id);
			BeanUtils.copyProperties(config,vo);
			log.info("mysql 返回");
			try {
				log.info("redis 保存");
				redisService.hashSetValue(KVRedisConfig.MES_POLLING_CONFIG.getHaskey(), 
						id,
						JsonUtil.changeObjectToJson(vo));
			} catch (JsonProcessingException e) {
				e.printStackTrace();
				log.error("redis存储异常，查看："+vo.getId());
			}
		}
		return vo;
	}
	
	@Override
	public MesPollingConfigVO findConfigByIdForShow(String id) {

		MesPollingConfigVO vo = new MesPollingConfigVO();
		
		MesPollingConfig config = pollingConfigRespository.findOne(id);
		
		BeanUtils.copyProperties(config,vo);
		
		return vo;
	}

	@Transactional
	@Override
	public void edit(MesPollingConfigVO vo) {
		MesPollingConfig reportPollingConfig = new MesPollingConfig();
		BeanUtils.copyProperties(vo,reportPollingConfig);
		pollingConfigRespository.save(reportPollingConfig);
		//让页面重新刷新缓存
		redisService.setConfig(KVRedisConfig.CHANGE_FONTSIZE.getHaskey(), "1");
		try {
			log.info("redis edit 保存");
			redisService.hashSetValue(KVRedisConfig.MES_POLLING_CONFIG.getHaskey(), 
					vo.getId(),
					JsonUtil.changeObjectToJson(vo));
			
			//保存url关联的id
			redisService.setConfig(vo.getKanbanUrl(), vo.getId());
			
			Map<String, String> map = redisService.jedis().hgetAll(KVRedisConfig.MES_POLLING_CONFIG.getHaskey()+ vo.getId());
			
			Set<String> keySet = map.keySet();
			
			Iterator<String> key = keySet.iterator();
			
			while (key.hasNext()) {
				redisService.jedis().hdel(KVRedisConfig.MES_POLLING_CONFIG.getHaskey()+ vo.getId(), key.next());
			}
			
		} catch (JsonProcessingException e) {
			e.printStackTrace();
			log.error("redis存储异常，查看："+vo.getId());
		}
		
	}
	
	@Transactional
	@Override
	public void deleteById(String id) {
		
		pollingConfigRespository.delete(id);
		
		redisService.jedis().hdel(KVRedisConfig.MES_POLLING_CONFIG.getHaskey(), id);
		
		Map<String, String> map = redisService.jedis().hgetAll(KVRedisConfig.MES_POLLING_CONFIG.getHaskey()+id);
		
		Set<String> keySet = map.keySet();
		
		Iterator<String> key = keySet.iterator();
		
		while (key.hasNext()) {
			redisService.jedis().hdel(KVRedisConfig.MES_POLLING_CONFIG.getHaskey()+id, key.next());
		}
		
		log.info("redis delete hashkey:"+KVRedisConfig.MES_POLLING_CONFIG.getHaskey()+" , key:"+id);
		
	}
	
	@Override
	public void deletes(String id,List<MesConfigList> cs) {

		this.deleteById(id);
		
		configListRespository.delete(cs);
	}
	
	 /** 
     * 两个时间相差距离多少天多少小时多少分多少秒 
     * @param str1 时间参数 1 格式：1990-01-01 12:00:00 
     * @param str2 时间参数 2 格式：2009-01-01 12:00:00 
     * @return long[] 返回值为：{天, 时, 分, 秒} 
     */  
    public static long getDistanceTimes(long one, long two) {  
        long time1 = one;  
		long time2 = two;  
		long diff ;  
		if(time1<time2) {  
		    diff = time2 - time1;  
		} else {  
		    diff = time1 - time2;  
		}  
        return diff;  
    }

	@Override
	public Map<String, Object> getConfigByUrl(String url) {
		
		//查询出URL对应的看板的素材播放配置信息
		Map<String, Object> map = new HashMap<String,Object>();
		
		//通过url查询redis中的配置信息
		List<MesConfigListVO> voLists = getConfigByUrlFromRedis(url);
		
		if(IsNotNullValit.CollectionIsNotNull(voLists)) {
			MesConfigListVO vo = voLists.get(0);
			log.info("查询redis的时间段");
			//步骤一  查看REDIS是否有当前看板播放内容及时间的缓存信息   
			//distanceTimes:定时器刷新时间  lastTime 当前播放的最后时间或下次播放的开始时间（long）  showType播放素材的类型  materialAddress 素材实际地址
			getTimeAndShowAndMaterialByRedis(vo,map);
			
			//步骤二 REDIS没有缓存信息，则从数据库读取配置
			//如果素材类型为空：情况1.第一次进来，得查询数据库   情况2.刷新页面时，当前时间大于或者小于REDIS中存储的当前看板播放素材的播放时间
			if(!IsNotNullValit.StringIsNotNull((String)map.get("showType"))) {
				
				//加载配置  将看板播放配置信息存储到REDIS缓存中去
				//KEY的规则   MES_CURRENT_CONFIG+voList.get(0).getPolingConfigId();  
				//VALUE的规则      素材的   开始播放时间=结束播放时间=素材类型
				log.info("redis中没有数据，读取数据库的配置信息");
				loadConfig(voLists,map);
				
			}
			
		}
		
		return map;
	}

	/**
	 * 加载配置  将看板播放配置信息存储到REDIS缓存中去
	 * KEY的规则   MES_CURRENT_CONFIG+voList.get(0).getPolingConfigId();  
	 * VALUE的规则      素材的   开始播放时间=结束播放时间=素材类型
	 * @param voList
	 * @param map
	 */
	private void loadConfig(List<MesConfigListVO> voList, Map<String, Object> map) {
		
		try {
			// 获得当前的年月日
			String nowYMD = DateUtil.getCurrentDate(new Date());

			// 开始播放时间
			long startTime = DateUtil.getDateByStr(nowYMD, "yyyy-MM-dd HH:mm:ss").getTime();
			// 如果lastTime不为空，则将值赋给startTime
			if (map.get("lastTime") != null) {
				startTime = (Long) map.get("lastTime");
			}
			if (!voList.isEmpty()) {

				MesConfigListVO vo = voList.get(0);

				/***** 计算定时器的时间 *****/
				log.info("计算定时器的时间");
				calculationTime(vo, startTime, map);

				/***** 已经更新REDIS中 MES_POLLING_CONFIG 看板素材配置的List集合 *****/
				String configId = vo.getPollingConfigId();
				// 从voList中移除当前信息
				voList.remove(0);
				if (!voList.isEmpty()) {
					log.info("集合不为为空，更新配置MES_POLLING_CONFIG");
					redisService.jedis().hset(KVRedisConfig.MES_POLLING_CONFIG.getHaskey().getBytes(),
							configId.getBytes(), SerializeUtil.serializeList(voList));
				} else {
					log.info("集合为空，删除配置MES_POLLING_CONFIG");
					redisService.jedis().hdel(KVRedisConfig.MES_POLLING_CONFIG.getHaskey().getBytes(),
							configId.getBytes());
				}

			}
		} catch (Exception e) {
			e.printStackTrace();
			throw new RuntimeException();
		}
		
	}

	/**
	 * 计算定时器的时间   获取当前播放素材的类型  如果素材为video ppt pic则还要获取素材的播放地址
	 * @param vo
	 * @param startTime
	 * @param map
	 */
	private void calculationTime(MesConfigListVO vo,Long startTime,Map<String, Object> map) {
		
		/*****计算改素材播放配置的时间段*****/
		//当前素材的播放时长
		int durantion = 0;
		
		String[] dus = vo.getShowDuration().split(",");
		
		for (int i = 0; i < dus.length; i++) {
			String timeStr = dus[i].equals("")?"0":dus[i];
			switch (i) {
			case 0:
				
				durantion += (Integer.valueOf(timeStr)*60*60);
				continue;
			case 1:
				durantion += (Integer.valueOf(timeStr)*60);
				continue;
			case 2:
				durantion += Integer.valueOf(timeStr);
				continue;

			default:
				break;
			}
		}
		
		//当前素材的结束播放时间
		Long endTime = DateUtil.addSecondForLong(new Date(startTime), durantion);
		
		// 将时间点存入到REDIS缓存

		String sdYMD = DateUtil.getCurrentDate(new Date(startTime));
		String edYMD = DateUtil.getCurrentDate(new Date(endTime));

		// 存入当前播放素材类型，以及其播放的时间段
		redisService.setConfig(KVRedisConfig.MES_CURRENT_CONFIG.getHaskey() + vo.getPollingConfigId(), sdYMD + "=" + edYMD + "=" + vo.getShowType());
		
		Date date = DateUtil.getDate();
		long currentTime = date.getTime();
		long sd = DateUtil.getCurrentDate(sdYMD, null).getTime();
		long ed = DateUtil.getCurrentDate(edYMD, null).getTime();
		
		// 如果在区间内
		if (currentTime > sd && currentTime < ed) {
			long distanceTimes = getDistanceTimes(currentTime, ed);
			map.put("distanceTimes", distanceTimes);
			map.put("showType",vo.getShowType());
			//如果是表格，返回数据刷新时间
			if("table".equals(vo.getShowType())) {
				//TODO 如果是表格，返回数据刷新时间
				map.put("tableTimer",vo.getTableTimer());
				redisService.setConfig("tableTimer", String.valueOf(vo.getTableTimer()));
			}else {
				//TODO 如果不是表格，则返回素材地址
				MesMaterial material = materialRespository.findOne(vo.getMaterialId());
				//素材地址
				map.put("materialName", material.getMaterialName());
				redisService.setConfig("materialName", material.getMaterialName());
			}
		} else if (currentTime > ed) {
			// 把这次播放的最后时间既是下次播放的开始时间返回（long）
			map.put("lastTime", currentTime);
		}else {
			map.put("lastTime", sd);
		}
		
	}

	/**
	 * 根据看板ID 查询REDIS中看板现在的播放的素材类型及时间段 以及素材的地址
	 * @param polingConfigId
	 */
	private void getTimeAndShowAndMaterialByRedis(MesConfigListVO vo,Map<String, Object> map) {
		
		// 获取存在redis中的素材地址
		String materialName = redisService.getConfig("materialName");
		map.put("materialName", materialName);
		
		//KEY的规则   MES_CURRENT_CONFIG+voList.get(0).getPolingConfigId();  
		//VALUE的规则      素材的   开始播放时间=结束播放时间=素材类型   
		String timeString = redisService.getConfig(KVRedisConfig.MES_CURRENT_CONFIG.getHaskey()+vo.getPollingConfigId());
		if(IsNotNullValit.StringIsNotNull(timeString)) {
			Date date  = DateUtil.getDate();
			long currentTime = date.getTime();
			
			String startTime = timeString.split("=")[0];
			String endTime = timeString.split("=")[1];
			//播放素材类型
			String showType = timeString.split("=")[2];
			
			long sd = DateUtil.getCurrentDate(startTime,null).getTime();
			long ed = DateUtil.getCurrentDate(endTime,null).getTime();
			
			//如果在区间内
			if(currentTime>sd && currentTime < ed) {
				//定时器刷新时间
				long distanceTimes = getDistanceTimes(currentTime,ed);
				map.put("distanceTimes",distanceTimes);
				map.put("showType", showType);
				String tableTimer = redisService.getConfig("tableTimer");
				if(IsNotNullValit.StringIsNotNull(tableTimer)) {
					map.put("tableTimer", Long.valueOf(tableTimer));
					log.info("tableTimer"+tableTimer);
				}
				log.info("distanceTimes"+distanceTimes);
			}else if (currentTime > ed) {
				//把这次播放的最后时间既是下次播放的开始时间返回（long）
				map.put("lastTime",currentTime);
			}else {
				map.put("lastTime", sd);
			}
		}
		
	}

	/**
	 * 通过URL查询REDIS配置信息
	 * @param url
	 * @return List<MesConfigListVO>
	 */
	@SuppressWarnings("unchecked")
	private List<MesConfigListVO> getConfigByUrlFromRedis(String url) {
		List<MesConfigListVO> voList = null;
		
		//根据URL在REDIS缓存中读取配置表 ID 和 URL 的对应关系
		String id = redisService.getConfig(url);
		
		//如果存在，则从REDIS缓存中取数据
		if(IsNotNullValit.StringIsNotNull(id)) {
			log.info("查询redis的看板的素材播放配置");
			//通过REDIS  取出看板相关的配置 
			byte[] value = redisService.jedis().hget(KVRedisConfig.MES_POLLING_CONFIG.getHaskey().getBytes(),id.getBytes());
			if(value!=null) {
				try {
					voList = (List<MesConfigListVO>) SerializeUtil.unserializeList(value);
					log.info("redis 根据url 返回了值，url："+value);
					//如果从REDIS查询的数据配置信息为空，则查询数据库
					if(!IsNotNullValit.CollectionIsNotNull(voList)) {
						
						log.info("redis 根据url查询出的数据为空，需要从数据库加载配置信息重新添加到REDIS中去");
						voList = this.getConfigListByUrl(url);
						
						if(IsNotNullValit.CollectionIsNotNull(voList)) {
							
							redisService.setConfig(url, voList.get(0).getPollingConfigId());
							
							log.info("redis 存储了url跟看板的关联关系：url:"+url+" , id:"+voList.get(0).getPollingConfigId());
							redisService.hashSetValue(KVRedisConfig.MES_POLLING_CONFIG.getHaskey(),id,JsonUtil.changeObjectToJson(voList));
							log.info("redis 存储了看板跟素材播放配置的关联关系：url:"+url+" , id:"+voList.get(0).getPollingConfigId());
						}
					}
				} catch (Exception e) {
					e.printStackTrace();
					log.error("redis 转换异常："+e.getMessage());
					//如果配置不存在，则从数据库查询出来返回，并将新的配置写入到REDIS
					voList = this.getConfigListByUrl(url);
					if(IsNotNullValit.CollectionIsNotNull(voList)) {
						redisService.setConfig(url, voList.get(0).getPollingConfigId());
						
						log.info("redis 存储了url跟地址的关联关系：url:"+url+" , id:"+voList.get(0).getPollingConfigId());
						try {
							redisService.hashSetValue(KVRedisConfig.MES_POLLING_CONFIG.getHaskey(),id,JsonUtil.changeObjectToJson(voList));
						} catch (JsonProcessingException e1) {
							e1.printStackTrace();
							log.error("redis 转换异常："+e1.getMessage());
						}
						log.info("redis 存储了看板跟素材播放配置的关联关系：url:"+url+" , id:"+voList.get(0).getPollingConfigId());
					}
					
				}
			}else {
				voList = this.getConfigListByUrl(url);
				if(IsNotNullValit.CollectionIsNotNull(voList)) {
					redisService.setConfig(url, voList.get(0).getPollingConfigId());
					try {
						redisService.hashSetValue(KVRedisConfig.MES_POLLING_CONFIG.getHaskey(),id,JsonUtil.changeObjectToJson(voList));
					} catch (JsonProcessingException e2) {
						e2.printStackTrace();
						log.error("redis 转换异常："+e2.getMessage());
					}
					log.info("redis 存储了看板跟素材播放配置的关联关系：url:"+url+" , id:"+voList.get(0).getPollingConfigId());
				}
			}
		}else {
			//如果配置不存在，则从数据库查询出来返回，并将新的配置写入到REDIS
			log.info("查询数据库的看板的素材播放配置");
			voList = this.getConfigListByUrl(url);
			if(IsNotNullValit.CollectionIsNotNull(voList)) {
				redisService.setConfig(url, voList.get(0).getPollingConfigId());
				log.info("redis 存储了url跟地址的关联关系：url:"+url+" , id:"+voList.get(0).getPollingConfigId());
			}
		}
		return voList;
	}
	
	
	/**
	 * 通过URL查询数据库配置信息
	 * @param url
	 * @return List<MesConfigListVO>
	 */
	public List<MesConfigListVO> getConfigListByUrl(String url) {
		
		//根据URL查询看板
		List<MesPollingConfig> pollConfigs = pollingConfigRespository.findMRConifgByUrl(url);
		
		List<MesConfigListVO> configVos = null;
		
		if (IsNotNullValit.CollectionIsNotNull(pollConfigs)) {
			
			configVos = new ArrayList<MesConfigListVO>();
			
			//查询出该看板的配置
			MesPollingConfig pollingConfig =  pollConfigs.get(0);
			
			List<MesConfigList> configLists = configListRespository.getConfigListByPolingConfigId(pollingConfig.getId());
			
			MesConfigListVO vo = null;
			if(IsNotNullValit.CollectionIsNotNull(configLists)) {
				for(MesConfigList config : configLists) {
					vo = new MesConfigListVO();
					BeanUtils.copyProperties(config, vo);
					vo.setKanbanPoition(pollingConfig.getKanbanPoition());
					vo.setKanbanUrl(pollingConfig.getKanbanUrl());
					configVos.add(vo);
				}
			}
			
		}
		
		return configVos;
	}

	@Override
	public PageBean findPollingConfigForPageBean(Integer pageNum, Integer pageSize,String userId) {
		
		PageBean pb = new PageBean();
		
		//看板查询权限编码
		String jobCode = JobConfig.AUTH_POLLINGCONFIG_MANAGE.getkey();
		//通过用户ID查询对应的作业列表
		List<String> jobContentList = userJobRespository.findJobContentByUserId(userId,jobCode);
		
		List<MesPollingConfig> list  = this.pollingConfigRespository.findConfigList(jobContentList);
		List<MesPollingConfig> us  = this.pollingConfigRespository.findConfigListByPage((pageNum-1)*pageSize,pageSize,jobContentList);
		int allRow = list.size();
		int totalPage = (allRow/pageSize) + (allRow%pageSize==0?0:1);
		
		pb.setAllCounts(allRow);
		pb.setPageSize(pageSize);
		pb.setCurrentPage(pageNum);
		pb.setTotalPage(totalPage);
		pb.setList(us);
		return pb;
		
	}

	@Override
	public MesPollingConfigVO findByKanbanUrl(String url) {
		log.info("PollingConfigServiceImpl findByKanbanUrl 通过看板url查询看板配置");
		MesPollingConfig config = pollingConfigRespository.findByKanbanUrl(url);
		MesPollingConfigVO vo = new MesPollingConfigVO();
		BeanUtils.copyProperties(config, vo);
		return vo;
	}
	
	
}
